package repository

import (
	"context"
	"database/sql"
	"go.uber.org/zap"
	"jk-time/webook/internal/cache"
	"jk-time/webook/internal/dao"
	"jk-time/webook/internal/domain"
	"time"
)

var (
	ErrDuplicateUser = dao.ErrUserDuplicateEmailOrPhone
	ErrUserNotFound  = dao.ErrUserNotFound
)

type UserRepository interface {
	Create(ctx context.Context, u domain.User) error
	FindById(ctx context.Context, id int64) (domain.User, error)
	FindByEmail(ctx context.Context, email string) (domain.User, error)
	FindByPhone(ctx context.Context, phone string) (domain.User, error)
	FindByWechat(ctx context.Context, openId string) (domain.User, error)
	FindByGithub(ctx context.Context, id int64) (domain.User, error)
}
type CacheUserRepository struct {
	dao   dao.UserDao
	cache cache.UserCache
}

func NewUserRepository(dao dao.UserDao, c cache.UserCache) UserRepository {
	return &CacheUserRepository{dao: dao, cache: c}
}

func (repo *CacheUserRepository) Create(ctx context.Context, u domain.User) error {
	return repo.dao.Insert(ctx, repo.toEntity(u))
}
func (repo *CacheUserRepository) FindById(ctx context.Context, id int64) (domain.User, error) {
	u, err := repo.cache.Get(ctx, id)
	if err == nil {
		return u, err
	}

	ue, err := repo.dao.FindById(ctx, id)
	if err != nil {
		return domain.User{}, err
	}
	u = domain.User{
		Id:       ue.Id,
		Email:    ue.Email.String,
		Password: ue.Password,
	}
	err = repo.cache.Set(ctx, u)
	if err != nil {
		zap.L().Error("缓存数据库失败:", zap.Error(err))
	}
	return u, nil
}
func (repo *CacheUserRepository) FindByEmail(ctx context.Context, email string) (domain.User, error) {
	u, err := repo.dao.FindByEmail(ctx, email)
	if err != nil {
		return domain.User{}, err
	}
	return repo.toDomain(u), nil
}
func (repo *CacheUserRepository) FindByPhone(ctx context.Context, phone string) (domain.User, error) {
	u, err := repo.dao.FindByPhone(ctx, phone)
	if err != nil {
		return domain.User{}, err
	}
	return repo.toDomain(u), nil
}
func (repo *CacheUserRepository) FindByWechat(ctx context.Context, openId string) (domain.User, error) {
	u, err := repo.dao.FindByWechat(ctx, openId)
	if err != nil {
		return domain.User{}, err
	}
	return repo.toDomain(u), nil
}
func (repo *CacheUserRepository) FindByGithub(ctx context.Context, openId int64) (domain.User, error) {
	u, err := repo.dao.FindByGithub(ctx, openId)
	if err != nil {
		return domain.User{}, err
	}
	return repo.toDomain(u), nil
}
func (repo *CacheUserRepository) toDomain(u dao.User) domain.User {
	return domain.User{
		Id:       u.Id,
		Email:    u.Email.String,
		Phone:    u.Phone.String,
		Password: u.Password,
		Wechat: domain.Oauth2Info{
			OpenId:  u.WechatOpenID.String,
			UnionId: u.WechatUnionID.String,
		},
		Github: domain.Oauth2Info{
			GithubId: u.GithubId.Int64,
			NodeId:   u.NodeId.String,
		},
	}
}
func (repo *CacheUserRepository) toEntity(u domain.User) dao.User {
	return dao.User{
		Id:       u.Id,
		Email:    sql.NullString{String: u.Email, Valid: u.Email != ""},
		Phone:    sql.NullString{String: u.Phone, Valid: u.Phone != ""},
		Password: u.Password,
		WechatUnionID: sql.NullString{
			Valid:  u.Wechat.UnionId != "",
			String: u.Wechat.UnionId,
		},
		WechatOpenID: sql.NullString{
			Valid:  u.Wechat.OpenId != "",
			String: u.Wechat.OpenId,
		},
		GithubId: sql.NullInt64{
			Valid: u.Github.GithubId != 0,
			Int64: u.Github.GithubId,
		},
		NodeId: sql.NullString{
			Valid:  u.Github.NodeId != "",
			String: u.Github.NodeId,
		},
		Ctime: time.Now().UnixMilli(),
	}
}
